package sfs

import (
	"bytes"
	"compress/zlib"
	"encoding/binary"
	"errors"
	"fmt"
	"github.com/gorilla/websocket"
	"io"
	"wzgames/pkg/server/ws"
)

// 实现/wzgames/pkg/server/ws/msg_transmitter.go IMessageTransmitter
// 数据格式详见 smartfoxserver.com

type WSMessageTransmitter struct {
	dataSerializer IDataSerializer
}

func NewWSMessageTransmitter(serializer IDataSerializer) *WSMessageTransmitter {
	return &WSMessageTransmitter{dataSerializer: serializer}
}

// OnRecvMessage 解码客户端数据
func (wm *WSMessageTransmitter) OnRecvMessage(ses ws.ISession) (interface{}, error) {
	conn, ok := ses.Raw().(*websocket.Conn)

	if !ok || conn == nil {
		return nil, errors.New("websocket conn unavailable")
	}

	var messageType int
	var raw []byte
	var err error
	messageType, raw, err = conn.ReadMessage()
	dataLen := uint32(len(raw))

	if err != nil {
		return nil, err
	}

	if dataLen < 2 {
		return nil, errors.New("packet short size")
	}

	switch messageType {
	case websocket.BinaryMessage:
		headerByte := int(raw[0])
		if (headerByte & 128) == 0 {
			return nil, fmt.Errorf("unexpected header byte:%v", headerByte)
		}
		packetHeader := NewPacketHeaderFromBinary(headerByte)

		var dataSize, idx, end uint32
		if packetHeader.BigSized() {
			idx = 5
			dataSize = binary.BigEndian.Uint32(raw[1:idx])
		} else {
			idx = 3
			dataSize = uint32(binary.BigEndian.Uint16(raw[1:idx]))
		}
		end = idx + dataSize

		if packetHeader.Encrypted() {
		}
		if end >= dataLen {
			end = dataLen
		}
		msgData := raw[idx:end]
		if packetHeader.Compressed() {
			reader1, _ := zlib.NewReader(bytes.NewReader(msgData))
			DecodeBytes, _ := io.ReadAll(reader1)
			msgData = DecodeBytes
			reader1.Close()
		}
		if len(msgData) < 3 {
			return nil, fmt.Errorf("byte size is insufficient. size:%v", len(msgData))
		}

		var tsfsObj *TSfsObject
		tsfsObj, err = wm.dataSerializer.Binary2object(msgData)
		if err != nil {
			return nil, err
		}
		if tsfsObj.IsNull(ControllId) {
			return nil, errors.New("control id required")
		}
		if tsfsObj.IsNull(ActionId) {
			return nil, errors.New("action id required")
		}
		return tsfsObj, nil
	}

	return nil, errors.New("invalid message")
}

// OnSendMessage 数据封包发往客户端
func (wm *WSMessageTransmitter) OnSendMessage(ses ws.ISession, msg interface{}) error {
	conn, ok := ses.Raw().(*websocket.Conn)

	if !ok || conn == nil {
		return nil
	}

	xw := XWriter{}
	defer xw.Reset()

	switch m := msg.(type) {
	case *RawPacket:
		xw.WriteUByte(SfsObject)
		xw.WriteUShort(uint16(3))
		xw.WriteUTF(ControllId)
		xw.WriteUByte(Byte)
		xw.WriteUByte(m.ControlId)
		xw.WriteUTF(ActionId)
		xw.WriteUByte(Short)
		xw.WriteShort(int16(m.Id))
		xw.WriteUTF(ParamId)
		xw.WriteUByte(SfsObject)
		xw.Write(m.Content)
	default:

	}

	isCompressed := false
	isEncrypted := false

	binLen := xw.Len()
	if binLen > CompressionThreshold {
		xw.Compress()
		isCompressed = true
		binLen = xw.Len()
	}
	if binLen > MaxMessageSize {
		return fmt.Errorf("message size is too big: %v", binLen)
	}

	sizeBytes := ShortByteSize
	if binLen > 65535 {
		sizeBytes = IntByteSize
	}

	packetHeader := NewPacketHeader(isEncrypted, isCompressed, false, sizeBytes == IntByteSize)

	writeBuffer := XWriter{}
	defer writeBuffer.Reset()
	writeBuffer.WriteByte(int8(packetHeader.Encode()))
	if sizeBytes > ShortByteSize {
		writeBuffer.WriteInt(int32(binLen))
	} else {
		writeBuffer.WriteShort(int16(binLen))
	}
	xw.WriteTo(&writeBuffer)

	conn.WriteMessage(websocket.BinaryMessage, writeBuffer.Data())

	return nil
}
